//
//  TestTouchAndRendering.cpp
//  Nano
//
//  Created by Jeppe Nielsen on 8/24/13.
//  Copyright (c) 2013 Jeppe Nielsen. All rights reserved.
//

#include "Engine.hpp"
#include "GameState.hpp"
#include "GameWorld.hpp"
#include "DeferredRenderSystem.hpp"
#include "Touchable.hpp"
#include "Material.hpp"
#include "TextureComponent.hpp"
#include "TouchSystem.hpp"
#include "FirstPersonMoverSystem.hpp"
#include "Timer.hpp"
#include "BezierCurve1.hpp"
#include <cmath>
#include "MathHelper.hpp"

using namespace Nano;

Component(Flicker)
public:
    float Speed;
    float Period;
    BezierCurve1 curve;

    void Reset() {
        Speed = 1.0f;
        Period = 0.0f;
    }
};

class FlickerSystem : public GameSystem {
public:
    void Initialize() {
        AddComponent<Light>();
        AddComponent<Flicker>();
    }
    void Update(float dt) {
        for (ObjectCollection::const_iterator it = Objects().begin(); it!=Objects().end(); ++it) {
            GameObject* obj = *it;
            Flicker* flicker = obj->GetComponent<Flicker>();
            Light* light = obj->GetComponent<Light>();
            flicker->Period += flicker->Speed * dt;
            light->Brightness = flicker->curve.Evaluate(fmodf(flicker->Period, flicker->curve.nodes.GetMaxPosition()));
        }
    }

};



class TestTouchAndRendering : public GameState<TestTouchAndRendering> {
    void Initialize() {
        
        Input.TouchUp += event_handler(this, &TestTouchAndRendering::TouchUp);
        
        renderer = world.CreateSystem<DeferredRenderSystem>();
        
        world.CreateSystem<FirstPersonMoverSystem>()->Input = &Input;
        world.CreateSystem<TouchSystem>()->Input = &Input;
        world.CreateSystem<FlickerSystem>();
        
        GameObject* camera = world.CreateObject();
        camera->AddComponent<Camera>()->Viewport = Manager().Viewport();
        camera->AddComponent<Transform>()->Position = Vector3(0,0,18);
        camera->AddComponent<FirstPersonMover>()->MovementSpeed=0.03f;
        camera->GetComponent<Camera>()->Near = 1.0f;
        camera->GetComponent<Camera>()->Far = 2000.0f;
        
        
        cubeAsset = world.CreateObject();
        cubeAsset->AddComponent<Mesh>()->AddPlane(0, {100,100}, Box(0,0,20,20));//(0, 1, Colour::White());
        cubeAsset->AddComponent<Material>()->BlendMode = BlendMode::None;
        cubeAsset->AddComponent<TextureComponent>()->Texture().LoadFromPng("grass.png");
        
        GameObject* cubeAsset2 = world.CreateObject();
        cubeAsset2->AddComponent<Mesh>()->AddGeoSphere(0, 1, 10);
        
        for (int x = 0; x<1; x++) {
            for (int y=0; y<1; y++) {
                cubeInstance = world.CreateObject();
                cubeInstance->AddComponent<Transform>();
                cubeInstance->AddComponent<Mesh>(cubeAsset);//y & 1 && x & 1 ? cubeAsset2 : cubeAsset);
                cubeInstance->AddComponent<Material>(cubeAsset);
                cubeInstance->AddComponent<TextureComponent>(cubeAsset);
                cubeInstance->GetComponent<Transform>()->Position = Vector3(0,-3,0);
                 //cubeInstance->GetComponent<Transform>()->Scale = Vector3(100,0.1f,40);
                //cubeInstance->AddComponent<Touchable>()->Click += event_handler(this, &TestTouchAndRendering::SphereClicked);
            }
        }
        
        cube = world.CreateObject();
        cube->AddComponent<Transform>()->Position = {-5,2.5f,4};
        cube->AddComponent<Mesh>()->AddCube(0, {1,5,1});
        cube->AddComponent<Material>();
        cube->AddComponent<TextureComponent>(cubeAsset);
        
        cube = world.CreateObject();
        cube->AddComponent<Transform>()->Position = {0,0,0};
        cube->AddComponent<Mesh>()->AddCube(0, 1);
        cube->AddComponent<Material>();
        cube->AddComponent<TextureComponent>(cubeAsset);
        
        
        
        
        GameObject* flicker = world.CreateObject();
        BezierCurve1& curve = flicker->AddComponent<Flicker>()->curve;
        curve.Add(0, 0.0f, 0.0f, 0.0f);
        curve.Add(0.2f, 2.0f, 2.0f, 2.0f);
        curve.Add(0.4f, 1.0f, 1.0f, 1.0f);
        curve.Add(0.6f, 2.0f, 2.0f, 2.0f);
        curve.Add(1.6f, 0.0f, 0.0f, 0.0f);
        curve.Add(3.0f, 0.0f, 0.0f, 0.0f);
        
        
        int numberOfLights = 0;
        for (int x=-4; x<=4; x++) {
            for (int y=-4; y<=4; y++) {
                light = world.CreateObject();
                light->AddComponent<Transform>()->Position = Vector3(x*6,3,y*6);
                light->AddComponent<Light>()->Type = Light::LightType::Point;
                light->GetComponent<Light>()->Radius = 9;
                light->AddComponent<Mesh>();
                light->CloneComponent<Flicker>(flicker)->Period = MathHelper::Random()*3.0f;//Vector3(x,0,y).Length() * 0.2f+x*0.3f;
                
                numberOfLights++;
            }
        }
        std::cout<< numberOfLights<<std::endl;
    }
    
    void SphereClicked(TouchData touch) {
        touch.object->GetComponent<Transform>()->Position += touch.WorldNormal * 0.5f;
    }
    
    void Update(float dt) {
        static float rotation = 0;
        rotation+=dt;
        //light->GetComponent<Light>()->Radius = 14;// + sinf(rotation*3)*3;
        cube->GetComponent<Transform>()->Rotation = Quaternion::Create(rotation, Vector3(1,1,0).Normalized());
        world.Update(dt);
    }
    
    void Render() {
        glClearColor( 0.0f, 0.2f, 0.0f, 1.0f );
        glClear( GL_COLOR_BUFFER_BIT  | GL_DEPTH_BUFFER_BIT);
        glEnable(GL_CULL_FACE);
        
        renderTimer.Begin();
        renderer->Render();
        renderTime += renderTimer.End();
        renderCounter++;
        if (renderCounter>20) {
            renderTime /= renderCounter;
            std::cout<<"Render() time = " << (1.0 / renderTime) << std::endl;
            renderCounter=0;
        }
    }
    
    void TouchUp(TouchEvent e) {
        //Exit();
    }
    
    GameWorld world;
    DeferredRenderSystem* renderer;
    GameObject* cubeAsset;
    GameObject* cubeInstance;
    GameObject* light;
    GameObject* light2;
    GameObject* cube;
    
    Timer renderTimer;
    double renderTime;
    int renderCounter;
};


int main() {
    Engine e;
    e.Start<TestTouchAndRendering>(true);
}

